package com.ynkbny.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ynkbny.domain.ArgumentDevice;
import com.ynkbny.domain.FormulaConfiguration;
import com.ynkbny.domain.dto.ArgumentDeviceAddDTO;
import com.ynkbny.domain.dto.FormulaConfigurationAddDTO;
import com.ynkbny.domain.pojo.FormulaConfigurationArgumentVO;
import com.ynkbny.mapper.ArgumentDeviceMapper;
import com.ynkbny.mapper.FormulaConfigurationMapper;
import com.ynkbny.service.ArgumentDeviceService;
import com.ynkbny.service.FormulaConfigurationService;
import com.ynkbny.util.Res;
import com.ynkbny.util.RespStaticEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author YY
 * @description 针对表【formula_configuration(公式表)】的数据库操作Service实现
 * @createDate 2023-09-04 20:28:57
 */
@Slf4j
@Service
public class FormulaConfigurationServiceImpl extends ServiceImpl<FormulaConfigurationMapper, FormulaConfiguration>
        implements FormulaConfigurationService {

    @Resource
    FormulaConfigurationMapper formulaConfigurationMapper;

    @Resource
    private ArgumentDeviceMapper argumentDeviceMapper;

    @Resource
    private ArgumentDeviceService argumentDeviceService;

    /**
     * 添加公式
     *
     * @param formulaConfigurationAddDTO FormulaConfigurationAddDTO
     * @author YY
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean add(FormulaConfigurationAddDTO formulaConfigurationAddDTO) {
        try {
            // 得到采集配置项id
            String dataCollectConfigureId = formulaConfigurationAddDTO.getDatacollectconfigureId();
            // 获取引用的公式列表
            List<FormulaConfigurationArgumentVO> formulaList = formulaConfigurationAddDTO.getFormulaConfigurationArgumentVoList();

            // 将入参添加到公式列表
            FormulaConfigurationArgumentVO vo = new FormulaConfigurationArgumentVO();
            vo.setFormulaWeight(formulaConfigurationAddDTO.getFormulaWeight());
            vo.setFormulaId("");
            formulaList.add(vo);

            // 遍历
            formulaList.forEach(n -> {
                // 转换实体
                FormulaConfiguration formulaConfiguration = new FormulaConfiguration();
                BeanUtils.copyProperties(n, formulaConfiguration);
                // 设置采集配置项id
                formulaConfiguration.setDatacollectconfigureId(dataCollectConfigureId);
                // 存入公式表
                formulaConfigurationMapper.add(formulaConfiguration);
            });

            // 获取参数设备列表
            List<ArgumentDeviceAddDTO> argumentDeviceAddDTOList = formulaConfigurationAddDTO.getArgumentDeviceAddDTOList();

            // 遍历
            argumentDeviceAddDTOList.forEach(n -> {
                ArgumentDevice argumentDevice = new ArgumentDevice();
                // 参数列表实体转换
                BeanUtils.copyProperties(n, argumentDevice);
                // 设置采集配置项id
                argumentDevice.setDatacollectconfigureId(dataCollectConfigureId);
                // 设置创建时间
                argumentDevice.setCreateTime(new Date());
                // 设置更新时间
                argumentDevice.setUpdateTime(new Date());
                // 保存设备参数
                argumentDeviceMapper.insert(argumentDevice);
            });
        } catch (Exception e) {
            log.info(e.getMessage());
            return false;
        }
        return true;
    }

    /**
     * 修改公式
     *
     * @param formulaConfigurationAddDTO FormulaConfigurationAddDTO
     * @Author YY
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(FormulaConfigurationAddDTO formulaConfigurationAddDTO) {

        // 获取原公式中的参数设备，并删除引用
        LambdaQueryWrapper<ArgumentDevice> oldDeviceWrapper = new LambdaQueryWrapper<>();
        oldDeviceWrapper.eq(ArgumentDevice::getDatacollectconfigureId, formulaConfigurationAddDTO.getDatacollectconfigureId());
        argumentDeviceService.remove(oldDeviceWrapper);
        // 获取原公式中的参数公式，并删除引用
        LambdaQueryWrapper<FormulaConfiguration> oldFormulaWrapper = new LambdaQueryWrapper<>();
        oldFormulaWrapper.eq(FormulaConfiguration::getDatacollectconfigureId, formulaConfigurationAddDTO.getDatacollectconfigureId())
                .ne(FormulaConfiguration::getFormulaId, "");
        this.remove(oldFormulaWrapper);
        // 添加新公式中的参数设备
        List<ArgumentDevice> deviceList = new ArrayList<>();
        formulaConfigurationAddDTO.getArgumentDeviceAddDTOList().forEach(n -> {
            ArgumentDevice argumentDevice = new ArgumentDevice();
            BeanUtils.copyProperties(n, argumentDevice);
            argumentDevice.setDatacollectconfigureId(formulaConfigurationAddDTO.getDatacollectconfigureId());
            argumentDevice.setCreateTime(new Date());
            argumentDevice.setUpdateTime(new Date());
            deviceList.add(argumentDevice);
        });
        argumentDeviceService.saveBatch(deviceList);
        // 添加新公式中的参数公式
        List<FormulaConfiguration> formulaList = new ArrayList<>();
        formulaConfigurationAddDTO.getFormulaConfigurationArgumentVoList().forEach(n -> {
            FormulaConfiguration formulaConfiguration = new FormulaConfiguration();
            BeanUtils.copyProperties(n, formulaConfiguration);
            formulaConfiguration.setDatacollectconfigureId(formulaConfigurationAddDTO.getDatacollectconfigureId());
            formulaConfiguration.setCreateTime(new Date());
            formulaConfiguration.setUpdateTime(new Date());
            formulaList.add(formulaConfiguration);
        });
        this.saveBatch(formulaList);
        return true;
    }

    /**
     * 删除公式
     *
     * @param dataCollectConfigureId 采集数据项id
     * @Author YY
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Res<Object> delete(String dataCollectConfigureId) {
        try {
            // 通过采集配置项id，获取对应的公式列表
            List<FormulaConfiguration> formulaConfigurationList = getFormulaConfigurations(dataCollectConfigureId);

            // 构造查询器
            LambdaQueryWrapper<FormulaConfiguration> formulaConfigurationWrapper = new LambdaQueryWrapper<>();
            formulaConfigurationWrapper.eq(FormulaConfiguration::getFormulaId, dataCollectConfigureId);
            //通过公式列表查询其是否被引用
            List<FormulaConfiguration> selectList = formulaConfigurationMapper.selectList(formulaConfigurationWrapper);
            if (!ObjectUtils.isEmpty(selectList)) {
                return Res.fail("该公式已被引用：" + selectList.get(0).getDatacollectconfigureId());
            }

            // 删除查询到的公式列表
            this.removeBatchByIds(formulaConfigurationList);

            // 构造查询器
            LambdaQueryWrapper<ArgumentDevice> argumentDeviceWrapper = new LambdaQueryWrapper<>();
            argumentDeviceWrapper.in(ArgumentDevice::getDatacollectconfigureId, dataCollectConfigureId);
            // 删除ArgumentDevice中采集配置项为 入参 的数据
            argumentDeviceMapper.delete(argumentDeviceWrapper);

        } catch (Exception e) {
            log.info(e.getMessage());
            return Res.fail(RespStaticEnum.FAIL);
        }
        return Res.ok();
    }

    /**
     * 查询公式列表
     *
     * @param enterpriseCode 企业统一信用代码
     * @return List<FormulaConfigurationArgumentVO>
     * @Author YY
     */
    @Override
    public List<FormulaConfigurationArgumentVO> selectFormulaList(String enterpriseCode) {
        log.info("入参：{}", enterpriseCode);
        List<FormulaConfigurationArgumentVO> list = formulaConfigurationMapper.list(enterpriseCode);
        log.info("方法返回值：{}", list);
        return list;
    }

    /**
     * 查询一个公式
     *
     * @param dataCollectConfigureId 采集数据项id
     * @return FormulaConfigurationAddDTO
     * @Author YY
     */
    @Override
    public FormulaConfigurationAddDTO getOneFormula(String dataCollectConfigureId) {

        // 通过采集配置项id，获取对应的公式列表
        List<FormulaConfiguration> formulaConfigurationList = getFormulaConfigurations(dataCollectConfigureId);
        //
        List<String> collectConfigureIdList = new ArrayList<>();
        formulaConfigurationList.forEach(n -> collectConfigureIdList.add(n.getFormulaId()));
        collectConfigureIdList.add(dataCollectConfigureId);
        // 通过采集配置项idList查询公式列表
        List<FormulaConfigurationArgumentVO> listByIdList = formulaConfigurationMapper.listByDataCollectConfigureIdList(collectConfigureIdList);

        // 构造采集配置项id与名称的映射
        Map<String, String> formulaMap = new HashMap<>(listByIdList.size());
        listByIdList.forEach(n -> formulaMap.put(n.getFormulaId(), n.getFormulaName()));

        // 通过公式id列表查询设备信息 - ArgumentDeviceAddDTO
        List<ArgumentDeviceAddDTO> deviceList = argumentDeviceService.listByDataCollectConfigureId(dataCollectConfigureId);

        // 构造返回对象 - FormulaConfigurationAddDTO
        FormulaConfigurationAddDTO formulaConfigurationAddDTO = new FormulaConfigurationAddDTO();

        // 获取被引的公式列表
        List<FormulaConfigurationArgumentVO> collect = listByIdList.stream().filter(n -> !formulaMap.get(dataCollectConfigureId).equals(n.getFormulaName())).collect(Collectors.toList());

        // 遍历被引用的公式，设置被引用时的权重
        collect.forEach(n -> n.setFormulaWeight(formulaConfigurationList.stream().filter(f -> f.getFormulaId().equals(n.getFormulaId())).findFirst().get().getFormulaWeight()));
        // 采集配置项id为参数
        formulaConfigurationAddDTO.setDatacollectconfigureId(dataCollectConfigureId);
        // 采集配置项名称
        formulaConfigurationAddDTO.setDataCollectConfigureName(formulaMap.get(dataCollectConfigureId));
        // 被引的公式列表
        formulaConfigurationAddDTO.setFormulaConfigurationArgumentVoList(collect);
        // 设备列表
        formulaConfigurationAddDTO.setArgumentDeviceAddDTOList(deviceList);

        return formulaConfigurationAddDTO;
    }

    /**
     * 通过采集配置项id，获取对应的公式列表
     *
     * @param dataCollectConfigureId 采集配置项id
     * @return List<FormulaConfiguration>
     * @Author YY
     */
    private List<FormulaConfiguration> getFormulaConfigurations(String dataCollectConfigureId) {
        // 构造查询器
        LambdaQueryWrapper<FormulaConfiguration> formulaConfigurationWrapper = new LambdaQueryWrapper<>();
        formulaConfigurationWrapper.eq(FormulaConfiguration::getDatacollectconfigureId, dataCollectConfigureId);

        // 查询公式列表
        List<FormulaConfiguration> formulaConfigurationList = formulaConfigurationMapper.selectList(formulaConfigurationWrapper);
        return formulaConfigurationList;
    }

    /**
     * 没有引用公式的计算
     *
     * @param dataCollectConfigureId 采集配置项id
     * @return 公式计算结果
     * @Author YY
     */
    private Double countDataDeviceList(String dataCollectConfigureId, Map<String, Double> deviceDataMap) {
        // 查询设备信息
        List<ArgumentDeviceAddDTO> deviceList = argumentDeviceMapper.getList(dataCollectConfigureId);
        double sum = 0;
        // 不为空才计算
        if (!ObjectUtils.isEmpty(deviceList)) {
            // 计算设备的值乘以权重的累加和
            sum = deviceList.stream()
                    .mapToDouble(n -> deviceDataMap.get(n.getDeviceId()) * n.getWeight())
                    .sum();
        }
        return sum;
    }

    /**
     * 套用公式计算
     *
     * @param dataCollectConfigureId 采集配置项id
     * @param deviceDataMap          设备id和数据的映射
     * @return 公式计算结果
     * @Author YY
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Double countDataFormulaList(String dataCollectConfigureId, Map<String, Double> deviceDataMap) {
        double sum = 0;

        // 查询公式列表
        LambdaQueryWrapper<FormulaConfiguration> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(FormulaConfiguration::getDatacollectconfigureId, dataCollectConfigureId);
        List<FormulaConfiguration> formulaList = formulaConfigurationMapper.selectList(lambdaQueryWrapper);

        // 判断是否
        if (!ObjectUtils.isEmpty(formulaList)) {
            for (FormulaConfiguration formulaConfiguration : formulaList) {
                if (formulaConfiguration.getFormulaId().isEmpty()) {
                    // 计算该公式的设备
                    sum += countDataDeviceList(formulaConfiguration.getDatacollectconfigureId(), deviceDataMap);
                } else {
                    // 计算引用公式
                    sum += countDataFormulaList(formulaConfiguration.getFormulaId(), deviceDataMap) * formulaConfiguration.getFormulaWeight();
                }
            }
        }
        return sum;
    }

    /**
     * 通过采集数配置项id查询该公式涉及的所有设备
     *
     * @param dataCollectConfigureId 采集配置项id
     * @return 该公式涉及的所有设备列表
     * @Author YY
     */
    @Override
    public List<ArgumentDeviceAddDTO> getFormulaAllDevice(String dataCollectConfigureId) {
        // 创建设备集合
        List<ArgumentDeviceAddDTO> deviceList = new ArrayList<>();
        // 创建队列
        Queue<String> formulaId = new LinkedList<>();
        // 将参数入队
        formulaId.offer(dataCollectConfigureId);
        // 遍历队列
        while (!formulaId.isEmpty()) {
            // 出队
            String note = formulaId.poll();
            // 通过出队元素查询公式列表
            List<FormulaConfiguration> formulaConfigurations = formulaConfigurationMapper.listByDataId(note);

//            List<FormulaConfiguration> formulaConfigurations = formulaConfigurationMapper.recursiveListByDataId(note);
            // 判断公式列表是否为空
            if (!ObjectUtils.isEmpty(formulaConfigurations)) {
                // 遍历
                formulaConfigurations.forEach(n -> {
                    if (!n.getFormulaId().isEmpty()) {
                        // 将公式引用的公式放入队列
                        formulaId.offer(n.getFormulaId());
                    } else {
                        // 查询设备信息
                        List<ArgumentDeviceAddDTO> dtoList = argumentDeviceMapper.getList(n.getDatacollectconfigureId());
                        // 将公式直接使用的设备放入设备集合
                        deviceList.addAll(dtoList);
                    }
                });
            }
        }
        // 返回设备集合
        return deviceList;
    }
}
